﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using snat.proxy.items;
using snat.model.events;
using snat.proxy;


namespace snat.model
{

    public delegate void messageSent(Message m, List<BaseNode> recipients);
    public delegate void messageRecieved(Message m, BaseNode sender);

    public class BaseNode : Entity
    {

        //TODO TODO COMPARE TO - SAME FOR CONNECTION AND THE LIKE
        public messageSent onSend;
        public messageRecieved onRecieve;

        public readonly int InitalEdgeCount;
        public readonly int InitialOutEdgeCount;
        public readonly int InitialInEdgeCount;

        public int OutEdgeCount {
            get {
                return outEdges.Count;
            }
        }


        public int InEdgeCount {
            get {
                return inEdges.Count;
            }
        }

        public int EdgeCount {
            get {
                return inEdges.Count + outEdges.Count;
            }
        }


        private int messagesSent;
        public int MessagesSent {
            get {
                return messagesSent;
            }
        }
        private int messagesReceived;
        public int MessagesReceived {
            get {
                return messagesReceived;
            }
        }

        /// <summary>
        /// outEdges is a dictionary of the id of the target of the edge to the edge itself
        /// </summary>
        private Dictionary<int, Edge> outEdges = new Dictionary<int, Edge>();

        /// <summary>
        /// inEdges is a dictionary of the id of the originator of the edge to the edge itself
        /// </summary>
        private Dictionary<int, Edge> inEdges = new Dictionary<int, Edge>();

        public List<Edge> InEdges {
            get {
                return inEdges.Values.ToList<Edge>();
            }
        }


        public List<Edge> OutEdges {
            get {
                return outEdges.Values.ToList<Edge>();
            }
        }





        /// <summary>
        /// The set of all messages concerning this node through time. I.E., every message ever sent or
        /// received by this node.
        /// </summary>
        public ListProxy<Message> AllMessages { get; private set; }
        /// <summary>
        /// The set of all messages sent from this node throughout time
        /// </summary>
        public ListProxy<Message> AllMessagesSent { get; private set; }
        /// <summary>
        /// The set of all messages received by this node throughout time 
        /// </summary>
        public ListProxy<Message> allMessagesReceived { get; private set; }



        public BaseNode(Entity e, List<Edge> _inEdges = null, List<Edge> _outEdges = null) : this(e.ID, e.Name, _inEdges, _outEdges) { }


        public BaseNode(int _id, string _name, List<Edge> _inEdges = null, List<Edge> _outEdges = null)
            : base(_id, _name) {
            inEdges = new Dictionary<int, Edge>();
            outEdges = new Dictionary<int, Edge>();

            if (_inEdges != null) {
                inEdges = _inEdges.ToDictionary(v => v.From.ID, v => v);
            }
            else inEdges = new Dictionary<int, Edge>();
            InitialInEdgeCount = inEdges.Count;

            if (_outEdges != null) {
                outEdges = _outEdges.ToDictionary(v => v.To.ID, v => v);
            }
            else outEdges = new Dictionary<int, Edge>();
            InitialOutEdgeCount = outEdges.Count;
            InitalEdgeCount = InitialInEdgeCount + InitialOutEdgeCount;

            onSend += doSend;
            onRecieve += doRecieve;
        }


        public void addInEdge(BaseNode sender, Edge newEdge) {
            inEdges.Add(sender.ID, newEdge);
        }

        private void doSend(Message m, List<BaseNode> recipients) {
            messagesSent++;
            foreach (BaseNode recipient in recipients) {
                if (recipient != null && !outEdges.ContainsKey(recipient.ID)) {  //if we don't have an edge out to this person                    
                    Edge newEdge = new Edge(this, recipient, m.TimeStamp);
                    outEdges.Add(recipient.ID, newEdge);
                   
                    recipient.addInEdge(this, newEdge);
                    NetworkModel.Instance.Edges.Add(newEdge);
                    //   Console.WriteLine("New edge created");
                }
                //else existing edge re-used
            }
        }

        private void doRecieve(Message m, BaseNode sender) {
            if (sender != null && !inEdges.ContainsKey(sender.ID)) {
                Edge newEdge = new Edge(sender, this, m.TimeStamp);
                inEdges.Add(sender.ID, newEdge);
                NetworkModel.Instance.Edges.Add(newEdge);
            }
            messagesReceived++;
        }


        public override int CompareTo(object obj) {
            return ID.CompareTo((obj as BaseNode).ID);
        }

    }
}
